using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using NBusiness.CodeDom;
using NBusiness.ESharp.Compiler;
using NBusiness.CodeDom.Compiler;
using NBusiness.CodeDom.Services;

namespace NBusiness.ESharp
{
    [Serializable]
    public class ESharpAccess : EntityAccess
    {
        protected override void Attach()
        {
            foreach (EntityElement element in Elements)
            {
                if (element.Type == ESharpEntityElementTypes.AccessField)
                {
                    List<IAccessible> accessible = new List<IAccessible>();
                    accessible.AddRange((Parent as ESharpEntity).Fields.FindAll(f => { return f.Name == element.Value; }).Cast<IAccessible>());
                    accessible.AddRange((Parent as ESharpEntity).Relationships.FindAll(r => { return r.Name == element.Value && r.From == r.Parent; }).Cast<IAccessible>());
                    
                    if (accessible.Count == 0)
                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES4000, element));
                    else if (accessible.Count > 1)
                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES4004, element));
                    else
                        Accessible = accessible[0];

                }
                else if (element.Type == ESharpEntityElementTypes.AccessType)
                {
                    List<EntityUsing> usingStatements = new List<EntityUsing>();
                    if ((Parent.Parent as EntityFamily).Root.Using.ContainsKey(element.Path ?? ""))
                        usingStatements.AddRange((Parent.Parent as EntityFamily).Root.Using[element.Path ?? ""]);

                    EntityUsing thisNamespace = new ESharpUsing();
                    thisNamespace.Namespace = (Parent.Parent as EntityFamily).Fullname;
                    thisNamespace.Root = (Parent.Parent as EntityFamily).Root;
                    usingStatements.Add(thisNamespace);

                    IFrameworkService framework = Provider.GetService(typeof(IFrameworkService)) as IFrameworkService;
                    EntityAccess[] rules = framework.GetAccessRules();

                    string name = element.Value.ToLower();
                    var found = from r in rules
                                from u in usingStatements
                                where
                                    r.Name.ToLower() == u.Namespace.ToLower() + "." + name ||
                                    r.Name.ToLower() == u.Namespace.ToLower() + "." + name + "accessrule"
                                select r;

                    int count = found.Count();
                    if (count == 0)
                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5102, element));
                    else if (count > 1)
                        OnBuildError(new ESharpBuildError(ESharpErrorCode.ES5105, element));
                }
            }
        }

        protected override void InitializeAssignmentLogic()
        {
            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.AccessAllow,
                new AssignmentLogicHandler(AssignAllow) ));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.AccessType));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.AccessRole,
                new AssignmentLogicHandler(AssignRole) ));

            Assignments.Add(new AssignmentLogic(
                ESharpEntityElementTypes.AccessMethod,
                new AssignmentLogicHandler(AssignMethod) ));

            Assignments.Add(new AssignmentLogic( 
                ESharpEntityElementTypes.AccessField ));

            Assignments.Add(new AssignmentLogic( 
                ESharpEntityElementTypes.StatementEnd,
                PostAssignmentAction.Stop ));

            Assignments.Add(new AssignmentLogic( ESharpEntityElementTypes.WhiteSpace ));
            Assignments.Add(new AssignmentLogic( ESharpEntityElementTypes.Comment ));
        }

        private void AssignAllow(object sender, AssignmentLogicEventArgs e)
        {
            switch (e.Elements.Peek().Value)
            {
                case "allow":
                    Allow = true;
                    break;
                case "deny":
                    Allow = false;
                    break;
                default:
                    OnBuildError(new ESharpBuildError(ESharpErrorCode.ES4001, e.Elements.Peek()));
                    break;
            }
        }

        private void AssignRole(object sender, AssignmentLogicEventArgs e)
        {
            Role = e.Elements.Peek().Value.Trim('"');
        }

        private void AssignMethod(object sender, AssignmentLogicEventArgs e)
        {
            switch (e.Elements.Peek().Value)
            {
                case "get":
                    Method = EntityAccessMethod.Get;
                    break;
                case "set":
                    Method = EntityAccessMethod.Set;
                    break;
                default:
                    Method = EntityAccessMethod.Unknown;
                    OnBuildError(new ESharpBuildError(ESharpErrorCode.ES4002, e.Elements.Peek()));
                    break;
            }
        }
    }
}
